fix(progress-spinner)!: errors following a full review of the component (#DS-5482) - #1979
Conversation
`size` was the last accessor input on the spinner, and the reason the automated signal migration skipped it: its setter stored the size and computed the SVG circle radius in one go. The radius is a `computed` now and `size` is a plain `input()`, typed `ProgressSpinnerSize` instead of an arbitrary string — which resolves a TODO that predates this review. `value` gained `numberAttribute`: `value="40"` used to pass the string `"40"`, which the percentage arithmetic coerced by accident. `percentage`, `dashOffsetPercent` and `svgCircleRadius` are `protected`. They are derived from the inputs the consumer already binds, and the last one is SVG geometry rather than a contract. The generated `id` comes from the CDK `_IdGenerator` instead of a module-level counter, so it no longer collides across lazily loaded bundles. `KbqLoaderOverlay.spinnerSize` reports `ProgressSpinnerSize` rather than `string` to keep feeding the narrowed input. BREAKING CHANGE: `KbqProgressSpinner.size` is a signal and no longer accepts an arbitrary string; `percentage`, `dashOffsetPercent` and `svgCircleRadius` are protected; `value` is a `numberAttribute` input, so a `null` binding yields `NaN` where it used to clamp to `0`. Reported and partly rewritten by the `progress-spinner-signals` schematic. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Visit the preview URL for this PR (updated for commit aeafe75): https://koobiq-next--prs-1979-k1vbiog4.web.app (expires Fri, 11 Sep 2026 12:45:47 GMT) 🔥 via Firebase Hosting GitHub Action 🌎 Sign: c9e37e518febda70d0317d07e8ceb35ac43c534c |
lskramarov
left a comment
There was a problem hiding this comment.
Code review — max effort
15 findings, ranked most severe first. Thirteen are correctness, one is test coverage, one is the
migration guide.
The two that block: the schematic never writes anything under ng update
(index.ts:394), and numberAttribute without a fallback makes a nullish [value] paint a full ring
instead of an empty one (progress-spinner.component.ts:64).
Most of the rest are guards that exist in the newer sibling migrations (navbar-signals-and-aria,
button-toggle-signals-and-aria) but not in this clone of the older alert-signals: the assignment
lookahead in the ref regex, variableScope for block-scoped locals, initializerTypeOf for
inject()/viewChild() receivers, and the template-side manual-member report.
Posted by Claude Code.
`numberAttribute` with no fallback turned a null binding into `NaN`, and the value feeds a `stroke-dashoffset` percentage — `NaN%` is not a CSS length, so the browser dropped the declaration and the circle rendered full. It falls back to 0, which is what the pre-migration input did. The schematic counted a `.html` file as a consumer only when it had a `size` read to rewrite, so a template-only consumer never heard that `size` no longer accepts an arbitrary string. Tests: the uniqueness assertion compared a generated id against a bound one, so it passed without testing uniqueness; and nothing covered `value="40"` as a static attribute, which is the case the transform exists for. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The autocomplete (#1985) and badge (#1978) reviews landed on main in the meantime, so their entries and subsections came with it: - migrations.json / collection.json: kept main's entries and appended progress-spinner-signals. - migration.en.md / migration.ru.md: main's block here runs Autocomplete, Badge, Notification center, Popover, all of which sort before Progress spinner, so this subsection goes after them rather than before. `check-public-api-any` counted progress-spinner 0 -> 1 on the merged tree. The `unknown` was the hand-written `value` transform, not an Angular signature, so it is narrowed to `number | string | null | undefined` instead of recorded: that keeps `[value]="progress$ | async"` and `value="40"` type-checking while an object or an array stays a template type error, which is the static check the review said this PR had dropped.
There was a problem hiding this comment.
🔵 Needs a closer look
It introduces a breaking API refactor plus a large new schematic-based migration, which warrants final human review despite the added tests and docs.
Pull request overview
This PR completes a full review of progress-spinner by finalizing its signal-based API, tightening its public types, and providing an ng update migration to rewrite consumer reads and report manual work. It also updates documentation and a dependent component (loader-overlay) to align with the narrowed spinner size input.
Changes:
- Refactors
KbqProgressSpinnerto a fully signal-based surface:sizebecomes aninput(),valuegains a typednumberAttributetransform, and derived internals becomeprotectedsignals. - Switches generated IDs to CDK
_IdGeneratorto avoid cross-bundle collisions. - Adds the
progress-spinner-signalsmigration (schema, implementation, tests) and documents it in the v20 migration guides.
File summaries
| File | Description |
|---|---|
| tools/public_api_guard/components/progress-spinner.api.md | Updates the extracted public API snapshot for the new signal-based members and narrowed ProgressSpinnerSize. |
| tools/public_api_guard/components/loader-overlay.api.md | Updates API snapshot to reflect spinnerSize now returning ProgressSpinnerSize. |
| packages/schematics/src/migrations/progress-spinner-signals/schema.ts | Adds TS schema interface for the new migration options. |
| packages/schematics/src/migrations/progress-spinner-signals/schema.json | Adds JSON schema for the migration (project, fix). |
| packages/schematics/src/migrations/progress-spinner-signals/README.md | Documents what the migration rewrites, reports, and how to run it. |
| packages/schematics/src/migrations/progress-spinner-signals/index.ts | Implements the migration: TS receiver detection + template ref rewriting + warnings/summary output. |
| packages/schematics/src/migrations/progress-spinner-signals/index.spec.ts | Adds a dedicated test suite covering rewrites, warnings, idempotence, and --fix=false. |
| packages/schematics/src/migrations/progress-spinner-signals/data.ts | Defines migration constants, warning patterns, and summary text. |
| packages/schematics/src/migrations.json | Registers progress-spinner-signals for 20.3.0-0. |
| packages/schematics/src/collection.json | Exposes the migration as a schematic with its schema. |
| packages/components/progress-spinner/progress-spinner.component.ts | Refactors spinner inputs to signals, adds typed coercion for value, makes derived values protected, and adopts _IdGenerator. |
| packages/components/progress-spinner/progress-spinner.component.spec.ts | Reworks unit tests to assert rendered output (dashoffset/radius/classes) and new coercion/id behavior. |
| packages/components/progress-spinner/progress-spinner.component.html | Updates template reads to call computed signals (svgCircleRadius(), dashOffsetPercent()). |
| packages/components/loader-overlay/loader-overlay.component.ts | Narrows spinnerSize to ProgressSpinnerSize to match the spinner’s tightened input type. |
| docs/guides/migration.ru.md | Documents the progress-spinner migration and the behavior/type changes (RU). |
| docs/guides/migration.en.md | Documents the progress-spinner migration and the behavior/type changes (EN). |
Review details
- Files reviewed: 16/16 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
20.3.0 was released on main, so #2005 moved every staged migration to 21.0.0-0. Two things the merge needed beyond the automatic resolution: - progress-spinner-signals merged in still registered for 20.3.0-0, which git had no reason to touch because the entry is an addition. At that version the migration would never run for anyone upgrading to 21, so it joins the other 28 on 21.0.0-0, together with the version in its README and its data.ts docblock. - loader-overlay.api.md conflicted: this branch narrowed `spinnerSize` to `ProgressSpinnerSize` while main added the `surface` input. The file is generated, so it was regenerated with `approve-api` rather than merged by hand; the result carries both.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The markdown review (#1981) landed on main, so only its two registry entries conflicted, at the append point every review branch shares: kept main's entries and appended progress-spinner-signals after them. Both guides auto-merged correctly this time - main's block already ends with Popover and the Markdown subsection it added sorts before Progress spinner, so the alphabetical order came out right without help. The migration is already registered for 21.0.0-0 from the previous merge, so nothing to re-version.
What
A full review of
progress-spinner, in the same shape as the 20.3.0 component reviews.sizewas the last accessor input — and it did two thingsThat side effect is exactly why the automated signal migration skipped it. The radius is a
computednow,sizeis a plaininput(), and it is typedProgressSpinnerSizeinstead of an arbitrary string — resolving the//@TODO use Exclude<KbqDefaultSizes, 'normal'>that predates this review.valuegainednumberAttributevalue="40"used to pass the string"40";Math.min(100, '40')coerced it by accident. It is a number now.Closed internals
percentage,dashOffsetPercentandsvgCircleRadiusareprotected. The first two are derived from thevaluethe consumer already binds; the last is SVG geometry, not a contract._IdGeneratorThe generated
idcomes from the CDK_IdGeneratorinstead of a module-levellet id = 0, so it no longer collides across lazily loaded bundles. Thekbq-progress-spinner-<n>shape is unchanged.One line outside the component
KbqLoaderOverlay.spinnerSizereportsProgressSpinnerSizerather thanstring, to keep feeding the narrowed input. It is a narrowing of a getter's return type, so nothing that reads it breaks.Migration
progress-spinner-signalsruns fromng update @koobiq/components@20. It rewritessizereads to calls — on receivers typedKbqProgressSpinnerand through template reference variables on<kbq-progress-spinner>, in external and inline templates — and reports the rest.Documented in
docs/guides/migration.{en,ru}.md, section 18.Testing
progress-spinner.component.spec.tsrewritten: 8 → 10 tests. The old ones readpercentageoff the instance; they assert the renderedstroke-dashoffset, circle radius and host classes now, which is what the value actually drives.progress-spinner-signals/index.spec.ts: 13 tests — auto-fix, idempotence, receiver scoping, template refs, warnings, and the--fix=falsepath.packages/components(4994 tests) andpackages/schematics(447 tests) suites pass.check-apiis in sync.No e2e screenshots were regenerated: the e2e cases pass
size="compact"/size="big"as before, and the rendered geometry is unchanged.BREAKING CHANGE
🤖 Generated with Claude Code